#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
凯利公式计算器启动器
功能说明：用于启动Streamlit凯利公式计算器的启动器脚本，支持exe打包
作者：luohe
创建时间：2024-12-19
修改时间：2024-12-19
"""

import sys
import os
import subprocess
import tkinter as tk
from tkinter import messagebox, ttk
import threading
import webbrowser
import time
from pathlib import Path

class KellyCalculatorLauncher:
    """凯利公式计算器启动器"""
    
    def __init__(self):
        """初始化启动器"""
        self.root = tk.Tk()
        self.root.title("凯利公式计算器启动器")
        self.root.geometry("500x400")
        self.root.resizable(False, False)
        
        # 设置图标（如果有的话）
        try:
            self.root.iconbitmap(default="calculator.ico")
        except:
            pass
        
        self.streamlit_process = None
        self.server_url = "http://localhost:8501"
        
        self.setup_ui()
        self.check_environment()
    
    def setup_ui(self):
        """设置用户界面"""
        # 主标题
        title_frame = tk.Frame(self.root, bg="#f0f0f0", height=80)
        title_frame.pack(fill="x", padx=10, pady=10)
        title_frame.pack_propagate(False)
        
        title_label = tk.Label(
            title_frame, 
            text="📊 凯利公式计算器", 
            font=("Arial", 18, "bold"),
            bg="#f0f0f0",
            fg="#2c3e50"
        )
        title_label.pack(expand=True)
        
        subtitle_label = tk.Label(
            title_frame,
            text="专业的投资比例计算与风险分析工具",
            font=("Arial", 10),
            bg="#f0f0f0",
            fg="#7f8c8d"
        )
        subtitle_label.pack()
        
        # 状态显示区域
        status_frame = tk.LabelFrame(self.root, text="系统状态", padx=10, pady=10)
        status_frame.pack(fill="x", padx=10, pady=5)
        
        self.status_text = tk.Text(
            status_frame, 
            height=8, 
            width=60,
            font=("Consolas", 9),
            bg="#2c3e50",
            fg="#ecf0f1",
            insertbackground="#ecf0f1"
        )
        self.status_text.pack(fill="both", expand=True)
        
        # 滚动条
        scrollbar = tk.Scrollbar(status_frame)
        scrollbar.pack(side="right", fill="y")
        self.status_text.config(yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.status_text.yview)
        
        # 进度条
        self.progress = ttk.Progressbar(
            self.root, 
            mode='indeterminate',
            length=480
        )
        self.progress.pack(padx=10, pady=5)
        
        # 按钮区域
        button_frame = tk.Frame(self.root)
        button_frame.pack(fill="x", padx=10, pady=10)
        
        # 启动按钮
        self.start_button = tk.Button(
            button_frame,
            text="🚀 启动计算器",
            font=("Arial", 12, "bold"),
            bg="#27ae60",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.start_calculator
        )
        self.start_button.pack(side="left", padx=5)
        
        # 打开浏览器按钮
        self.browser_button = tk.Button(
            button_frame,
            text="🌐 打开浏览器",
            font=("Arial", 12),
            bg="#3498db",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.open_browser,
            state="disabled"
        )
        self.browser_button.pack(side="left", padx=5)
        
        # 停止按钮
        self.stop_button = tk.Button(
            button_frame,
            text="⏹️ 停止服务",
            font=("Arial", 12),
            bg="#e74c3c",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.stop_calculator,
            state="disabled"
        )
        self.stop_button.pack(side="left", padx=5)
        
        # 退出按钮
        exit_button = tk.Button(
            button_frame,
            text="❌ 退出",
            font=("Arial", 12),
            bg="#95a5a6",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.exit_application
        )
        exit_button.pack(side="right", padx=5)
        
        # 帮助按钮
        help_button = tk.Button(
            button_frame,
            text="❓ 帮助",
            font=("Arial", 12),
            bg="#f39c12",
            fg="white",
            relief="flat",
            padx=20,
            pady=10,
            command=self.show_help
        )
        help_button.pack(side="right", padx=5)
    
    def log_message(self, message: str, level: str = "INFO"):
        """记录日志消息"""
        timestamp = time.strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {level}: {message}\n"
        
        self.status_text.insert(tk.END, log_entry)
        self.status_text.see(tk.END)
        self.root.update()
    
    def check_environment(self):
        """检查运行环境"""
        self.log_message("正在检查运行环境...")
        
        # 检查Python版本
        python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
        self.log_message(f"Python版本: {python_version}")
        
        # 检查必要文件
        required_files = [
            "streamlit_kelly_calculator.py",
            "requirements.txt"
        ]
        
        missing_files = []
        for file in required_files:
            if not os.path.exists(file):
                missing_files.append(file)
            else:
                self.log_message(f"✓ 找到文件: {file}")
        
        if missing_files:
            self.log_message(f"❌ 缺少文件: {', '.join(missing_files)}", "ERROR")
            messagebox.showerror("文件缺失", f"缺少必要文件:\n{chr(10).join(missing_files)}")
            return False
        
        # 检查依赖包
        self.check_dependencies()
        
        self.log_message("✓ 环境检查完成")
        return True
    
    def check_dependencies(self):
        """检查依赖包"""
        self.log_message("正在检查依赖包...")
        
        required_packages = [
            "streamlit",
            "plotly",
            "numpy",
            "pandas"
        ]
        
        missing_packages = []
        
        for package in required_packages:
            try:
                __import__(package)
                self.log_message(f"✓ {package} 已安装")
            except ImportError:
                missing_packages.append(package)
                self.log_message(f"❌ {package} 未安装", "WARNING")
        
        if missing_packages:
            self.log_message("正在安装缺失的依赖包...", "INFO")
            self.install_dependencies(missing_packages)
    
    def install_dependencies(self, packages):
        """安装依赖包"""
        try:
            # 检测是否在exe环境中运行
            if getattr(sys, 'frozen', False):
                # 在exe环境中，需要使用python命令而不是sys.executable
                python_cmd = "python"
            else:
                # 在开发环境中，使用sys.executable
                python_cmd = sys.executable
                
            for package in packages:
                self.log_message(f"正在安装 {package}...")
                result = subprocess.run(
                    [python_cmd, "-m", "pip", "install", package],
                    capture_output=True,
                    text=True,
                    timeout=300
                )
                
                if result.returncode == 0:
                    self.log_message(f"✓ {package} 安装成功")
                else:
                    self.log_message(f"❌ {package} 安装失败: {result.stderr}", "ERROR")
        
        except subprocess.TimeoutExpired:
            self.log_message("安装超时，请检查网络连接", "ERROR")
        except Exception as e:
            self.log_message(f"安装过程出错: {str(e)}", "ERROR")
    
    def start_calculator(self):
        """启动计算器"""
        if self.streamlit_process and self.streamlit_process.poll() is None:
            self.log_message("计算器已在运行中", "WARNING")
            return
        
        self.log_message("正在启动凯利公式计算器...")
        self.progress.start()
        
        # 在新线程中启动
        thread = threading.Thread(target=self._start_streamlit)
        thread.daemon = True
        thread.start()
    
    def _start_streamlit(self):
        """在后台启动Streamlit"""
        try:
            # 检测是否在exe环境中运行
            if getattr(sys, 'frozen', False):
                # 在exe环境中，需要使用python命令而不是sys.executable
                python_cmd = "python"
            else:
                # 在开发环境中，使用sys.executable
                python_cmd = sys.executable
            
            # 启动Streamlit应用
            self.streamlit_process = subprocess.Popen(
                [python_cmd, "-m", "streamlit", "run", "streamlit_kelly_calculator.py", 
                 "--server.port=8501", "--server.headless=true"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True
            )
            
            # 等待服务启动
            time.sleep(3)
            
            if self.streamlit_process.poll() is None:
                self.log_message("✓ 计算器启动成功！")
                self.log_message(f"访问地址: {self.server_url}")
                
                # 更新按钮状态
                self.root.after(0, self._update_buttons_after_start)
                
                # 自动打开浏览器
                time.sleep(1)
                self.root.after(0, self.open_browser)
            else:
                self.log_message("❌ 计算器启动失败", "ERROR")
                self.root.after(0, self._update_buttons_after_error)
        
        except Exception as e:
            self.log_message(f"启动过程出错: {str(e)}", "ERROR")
            self.root.after(0, self._update_buttons_after_error)
        
        finally:
            self.root.after(0, lambda: self.progress.stop())
    
    def _update_buttons_after_start(self):
        """启动成功后更新按钮状态"""
        self.start_button.config(state="disabled")
        self.browser_button.config(state="normal")
        self.stop_button.config(state="normal")
    
    def _update_buttons_after_error(self):
        """启动失败后更新按钮状态"""
        self.start_button.config(state="normal")
        self.browser_button.config(state="disabled")
        self.stop_button.config(state="disabled")
    
    def open_browser(self):
        """打开浏览器"""
        try:
            webbrowser.open(self.server_url)
            self.log_message(f"已打开浏览器: {self.server_url}")
        except Exception as e:
            self.log_message(f"打开浏览器失败: {str(e)}", "ERROR")
    
    def stop_calculator(self):
        """停止计算器"""
        if self.streamlit_process:
            try:
                self.streamlit_process.terminate()
                self.streamlit_process.wait(timeout=5)
                self.log_message("✓ 计算器已停止")
            except subprocess.TimeoutExpired:
                self.streamlit_process.kill()
                self.log_message("强制停止计算器")
            except Exception as e:
                self.log_message(f"停止过程出错: {str(e)}", "ERROR")
            
            self.streamlit_process = None
        
        # 更新按钮状态
        self.start_button.config(state="normal")
        self.browser_button.config(state="disabled")
        self.stop_button.config(state="disabled")
    
    def show_help(self):
        """显示帮助信息"""
        help_text = """
凯利公式计算器使用说明

1. 点击"启动计算器"按钮启动Web应用
2. 应用启动后会自动打开浏览器
3. 在Web界面中输入交易参数：
   - 胜率：交易获胜的概率
   - 胜利收益率：获胜时的收益率
   - 失败损失率：失败时的损失率
4. 点击"开始计算"查看分析结果
5. 查看四种可视化分析图表
6. 使用完毕后点击"停止服务"关闭应用

注意事项：
- 确保8501端口未被占用
- 首次使用可能需要安装依赖包
- 建议使用Chrome或Edge浏览器

技术支持：luohe
版本：v2.0
        """
        
        help_window = tk.Toplevel(self.root)
        help_window.title("使用帮助")
        help_window.geometry("500x400")
        help_window.resizable(False, False)
        
        help_text_widget = tk.Text(
            help_window,
            wrap=tk.WORD,
            padx=10,
            pady=10,
            font=("Arial", 10)
        )
        help_text_widget.pack(fill="both", expand=True)
        help_text_widget.insert("1.0", help_text)
        help_text_widget.config(state="disabled")
    
    def exit_application(self):
        """退出应用"""
        if self.streamlit_process:
            self.stop_calculator()
        
        self.root.quit()
        self.root.destroy()
    
    def run(self):
        """运行启动器"""
        # 设置关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.exit_application)
        
        # 启动主循环
        self.root.mainloop()

def main():
    """主函数"""
    try:
        launcher = KellyCalculatorLauncher()
        launcher.run()
    except Exception as e:
        messagebox.showerror("启动错误", f"启动器运行出错:\n{str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    main()